﻿//SciChart.xaml.cs
//Copyright (c) 2013 StockSharp LLC, all rights reserved.
//This code module is part of StockSharp library.
//This code is licensed under the GNU GENERAL PUBLIC LICENSE Version 3.
//See the file License.txt for the license details.
//More info on: http://stocksharp.com

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;

using Abt.Controls.SciChart;

using Ecng.Collections;
using Ecng.Serialization;
using Ecng.Xaml;

namespace StockSharp.Xaml
{
	/// <summary>
	/// Графический компонент отображения свечных графиков.
	/// </summary>
	public partial class SciChart : IChart
	{
		private static int _lastId;
		private readonly int _id = ++_lastId;

		internal int ChartId
		{
			get { return _id; }
		}

		static SciChart()
		{
			MsChartEx.InitChartSettings();
		}

		/// <summary>
		/// Создать <see cref="SciChart" />.
		/// </summary>
		public SciChart()
		{
			InitializeComponent();

			_areas = new ChartAreaCollection(this);

			DataContext = _sciChartViewModel = new SciChartViewModel();
		}

		private readonly SciChartViewModel _sciChartViewModel;
		private readonly ChartAreaCollection _areas;

		/// <summary>
		/// Области графика.
		/// </summary>
		public INotifyList<ChartArea> Areas
		{
			get { return _areas; }
		}

		/// <summary>
		/// Скроллировать автоматически области <see cref="Areas" /> при появлении новых данных. По-умолчанию включено.
		/// </summary>
		public bool IsAutoScroll { get; set; }

		/// <summary>
		/// Использовать встроенные цвета.
		/// </summary>
		public bool UseThemeColors { get; set; }

		/// <summary>
		/// Название графической темы.
		/// </summary>
		public string ChartTheme
		{
			get { return _sciChartViewModel.SelectedTheme; }
			set { _sciChartViewModel.SelectedTheme = value; }
		}

		/// <summary>
		/// Сбросить отрисованные ранее значения элементов графика.
		/// </summary>
		/// <param name="elements">Элементы графика.</param>
		public void Reset(IEnumerable<IChartElement> elements)
		{
			Dispatcher.GuiAsync(() => _areas.Reset(elements));
		}

		/// <summary>
		/// Обработать новые данные.
		/// </summary>
		/// <param name="values">Новые данные.</param>
		public void ProcessValues(IDictionary<DateTime, IDictionary<IChartElement, object>> values)
		{
			_areas.ProcessValues(values);
		}

		internal void EnsureUIThread()
		{
			if (!CheckAccess())
				throw new InvalidOperationException("Operation is allowed for UI thread only.");
		}

		#region IPersistable

		/// <summary>
		///     Загрузить настройки.
		/// </summary>
		/// <param name="storage">Хранилище настроек.</param>
		public void Load(SettingsStorage storage)
		{
			IsAutoScroll = storage.GetValue("IsAutoScroll", true);
			ChartTheme = storage.GetValue("ChartTheme", "Chrome");
			_areas.Load(storage.GetValue<SettingsStorage>("Areas"));
		}

		/// <summary>
		///     Сохранить настройки.
		/// </summary>
		/// <param name="storage">Хранилище настроек.</param>
		public void Save(SettingsStorage storage)
		{
			storage.SetValue("IsAutoScroll", IsAutoScroll);
			storage.SetValue("ChartTheme", ChartTheme);
			storage.SetValue("Areas", _areas.Save());
		}

		#endregion

		private sealed class ChartAreaCollection : BaseList<ChartArea>, IPersistable
		{
			private readonly SciChart _chart;

			private readonly Dictionary<ChartArea, SciChartPaneViewModel> _chartPanes =
				new Dictionary<ChartArea, SciChartPaneViewModel>();

			public ChartAreaCollection(SciChart chart)
			{
				if (chart == null)
					throw new ArgumentNullException("chart");
				_chart = chart;
			}

			protected override bool OnAdding(ChartArea area)
			{
				_chart.EnsureUIThread();
				if (area == null || Contains(area))
					throw new ArgumentException("area");

				return base.OnAdding(area);
			}

			protected override void OnAdded(ChartArea area)
			{
				base.OnAdded(area);

				((INotifyPropertyChanged)area).PropertyChanged += AreaPropertyUpdated;

				var model = new SciChartPaneViewModel(_chart._sciChartViewModel, area, _chart);
				_chart._sciChartViewModel.ChartPaneViewModels.Add(model);
				_chartPanes.Add(area, model);
			}

			protected override void OnRemoved(ChartArea area)
			{
				_chart.EnsureUIThread();
				base.OnRemoved(area);

				var model = _chartPanes[area];
				_chart._sciChartViewModel.ChartPaneViewModels.Remove(model);
				_chartPanes.Remove(area);

				((INotifyPropertyChanged)area).PropertyChanged -= AreaPropertyUpdated;
			}

			protected override bool OnClearing()
			{
				_chart.EnsureUIThread();
				foreach (var p in _chartPanes.ToList())
				{
					_chart._sciChartViewModel.ChartPaneViewModels.Remove(p.Value);
					_chartPanes.Remove(p.Key);
				}
				_chart._sciChartViewModel.ChartPaneViewModels.Clear();
				_chartPanes.Clear();

				_chart._sciChartViewModel.XVisibleRange = new IndexRange(0, int.MaxValue);
				return base.OnClearing();
			}

			private static void AreaPropertyUpdated(object sender, PropertyChangedEventArgs e)
			{
			}

			public void ProcessValues(IDictionary<DateTime, IDictionary<IChartElement, object>> values)
			{
				if (values == null)
					throw new ArgumentNullException("values");

				foreach (var narea in _chartPanes.Values)
					narea.ProcessValues(values);
			}

			public void Reset(IEnumerable<IChartElement> elements)
			{
				foreach (var narea in _chartPanes.Values)
					narea.Reset(elements);
			}

			#region IPersistable

			public void Load(SettingsStorage storage)
			{
				this.AddRange(storage.GetValue<IEnumerable<SettingsStorage>>("Areas").Select(s =>
				{
					var area = new ChartArea();
					area.Load(s);
					return area;
				}));
			}

			public void Save(SettingsStorage storage)
			{
				storage.SetValue("Areas", this.Select(a =>
				{
					var s = a.Save();
					s.SetValue("Height", _chartPanes[a].Height);
					return s;
				}).ToArray());
			}

			#endregion
		}
	}
}